﻿/* MVT Charts for Silverlight - Version 1
 * 2008 by Mario Meir-Huber
 * mailto: i-mameir@microsoft.com
 *         mario@meirhuber.de
 */ 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using ChartLibrary.Base;
using ChartLibrary.Data;
using System.Diagnostics;

/*
 * ChartLibrary.Base is the main Namespace for charts
 */ 
namespace ChartLibrary.Base
{
    /// <summary>
    /// Base class for all Charts
    /// </summary>
    /// <history>
    ///     03/18/08: Implementation for Version 0.2
    /// </history>
    public abstract class ChartControl : UserControl
    {
        private DataModel _datamodel;           //model containing the Data
        private bool _displaytitle = false;     //stores information on the title
        private bool _displaylegend = false;    //information if legend will be displayed or not
        private Brush _background;              //brush used for the background
        private Brush _foreground;              //brush used for the foreground
        private Font _font;                     //information on appearance of the fonts
        private Legend _legend;                 //information on layout and appearance of the legend

        protected double AreaStartX, AreaStartY;        //X and Y Start Position
        protected double AreaWidth, AreaHeight;         //Width and Height property

        private ItemType _itemtype;             //Item Type
        private Animation _animation;           //Implements an Animation
        private Canvas _layoutroot;             //Displays everything

        public event StateChangedEvent DrawAreaChanged;     //Occurs when the area, on which the chart is displayed is changed
        public event MouseEventHandler LegendMouseEnter;    //Occurs when the mouse enters the Legend Area
        public event MouseEventHandler LegendMouseLeave;    //Occurs when the mouse leaves the Legend Area

        private string _title;          //Stores the Title

        /// <summary>
        /// Displays a new Chart Control
        /// </summary>
        public ChartControl()
        {
            _datamodel = new DataModel();
            _font = new Font();
            _legend = new Legend();
            _layoutroot = new Canvas();

            Area = new UseableArea();

            base.Content = _layoutroot;

            this.Loaded += new RoutedEventHandler(ChartControl_Loaded);
            this.SizeChanged += new SizeChangedEventHandler(ChartControl_SizeChanged);

            _font.DesignChanged += new StateChangedEvent(OnDesignChanged);
            _font.FontChanged += new StateChangedEvent(OnFontChanged);

            _legend.LegendChanged += new StateChangedEvent(OnLegendChanged);

            Foreground = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));
        }

        /// <summary>
        /// handles the LegendChangedEvent
        /// </summary>
        void OnLegendChanged()
        {
            if (_displaylegend)
            {
                RemoveLegend();
                DrawLegend();
            }
        }

        /// <summary>
        /// occurs when the font was changed
        /// </summary>
        void OnFontChanged()
        {
            DisplayTitle = !DisplayTitle;
            DisplayTitle = !DisplayTitle;
        }

        /// <summary>
        /// occurs when the design was changed
        /// </summary>
        void OnDesignChanged()
        {
            Rectangle tmp = _layoutroot.FindName("_legendBackground") as Rectangle;

            if (null != tmp)
            {
                tmp.Fill = _legend.Background;
            }
        }

        /// <summary>
        /// Occurs when the Size is changed
        /// PRIVATE
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ChartControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            UpdateDisplayedItems();
            UpdateMetrics();
        }

        /// <summary>
        /// Updates the metrics when the chart is loaded
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ChartControl_Loaded(object sender, RoutedEventArgs e)
        {
            UpdateMetrics();
        }

        /// <summary>
        /// Gets the Datamodel used for the Control
        /// </summary>
        public DataModel Data
        {
            get
            {
                return _datamodel;
            }
        }

        /// <summary>
        /// Gets the chart title or sets it
        /// </summary>
        public string Title
        {
            get
            {
                return _title;
            }
            set
            {
                TextBlock title;

                _title = value;

                title = LayoutRoot.FindName("_titletext") as TextBlock;

                if (null != title)
                {
                    title.Text = value;
                }
            }
        }

        /// <summary>
        /// Gets a value indicating if title is displayed or sets it
        /// </summary>
        public bool DisplayTitle
        {
            get
            {
                return _displaytitle;
            }
            set
            {
                if (value == _displaytitle) return;

                _displaytitle = value;

                if (_displaytitle)
                {
                    DrawTitle();
                }
                else
                {
                    RemoveTitle();
                }

                if (null != DrawAreaChanged) DrawAreaChanged();
            }
        }

        /// <summary>
        /// Draws the title for the chart.
        /// </summary>
        private void DrawTitle()
        {
            TextBlock display;
            //get object to set
            display = _layoutroot.FindName("_titletext") as TextBlock;

            //instantiates a new textblock if not yet present
            if (null == display)
            {
                display = new TextBlock();
                display.Text = Title;
                display.SetValue(TextBlock.NameProperty, "_titletext");
                display.Foreground = _foreground;
                
                if(!_layoutroot.Children.Contains(display))
                    _layoutroot.Children.Add(display);
            }
            else
            {
                display.Text = Title;
            }

            //set size, fontfamily ...
            display.FontSize = _font.TitleSize;
            display.FontFamily = _font.TitleFont;

            display.Visibility = Visibility.Visible;
            display.SetValue(Canvas.TopProperty, 10.0);
            display.SetValue(Canvas.LeftProperty, (_layoutroot.ActualWidth - display.ActualWidth) / 2);

            UpdateMetrics();
        }

        /// <summary>
        /// Removes the title for the chart, if the title is no more needed
        /// </summary>
        private void RemoveTitle()
        {
            TextBlock display;
            //removes textbox
            display = _layoutroot.FindName("_titletext") as TextBlock;

            if (null != display && !_displaytitle)
            {
                _layoutroot.Children.Remove(display);
            }

            UpdateMetrics();
        }

        /// <summary>
        /// Gets a value indicating if chart legend is displayed or sets it
        /// </summary>
        public bool DisplayLegend
        {
            get
            {
                return _displaylegend;
            }
            set
            {
                if (value == _displaylegend) return;

                if (null != Data)
                {
                    if (Data.Count <= 0) return;
                }
                else return;

                _displaylegend = value;


                //displays a legend
                if (_displaylegend)
                {
                    DrawLegend();
                }
                else
                {
                    RemoveLegend();
                }

                if (null != DrawAreaChanged) DrawAreaChanged();
            }
        }

        /// <summary>
        /// Remove the Legend that displays the Information on the chart
        /// </summary>
        private void RemoveLegend()
        {
            Canvas content;
            content = _layoutroot.FindName("_legendCanvas") as Canvas;

            if (null != content)
            {
                content.Children.Clear();
                _layoutroot.Children.Remove(content);
            }

            UpdateMetrics();
        }

        /// <summary>
        /// this redraws the Legend Canvas
        /// </summary>
        private void DrawLegend()
        {
            //variables used
            Canvas content;
            Rectangle rt;
            TextBlock tmptext;
            TextBlock txt;
            Line tmpline;

            //various positions
            double positiontop = 10.0;
            double positionleft = 0.0;
            double maxwidth = 0.0;
            double maxheight = 0.0;

            rt = new Rectangle();

            content = _layoutroot.FindName("_legendCanvas") as Canvas;

            if (null == content) content = new Canvas();
            else
            {
                _layoutroot.Children.Remove(content);
                content = new Canvas();
            }

            content.MouseEnter += new MouseEventHandler(OnLegendEnter);
            content.MouseLeave += new MouseEventHandler(OnMouseLeave);

            content.SetValue(Canvas.NameProperty, "_legendCanvas");

            rt.SetValue(Canvas.LeftProperty, 0.0);
            rt.SetValue(Canvas.TopProperty, 0.0);

            rt.SetValue(Canvas.NameProperty, "_legendBackground");

            content.Children.Add(rt);

            //sets legend
            switch (_legend.Layout)
            {
                case Layout.Horizontal:
                    {
                        foreach (DataSource ds in _datamodel)
                        {
                            tmptext = new TextBlock();
                            tmptext.Text = ds.Name;
                            tmptext.FontSize = 11;
                            tmptext.FontFamily = new FontFamily("Arial");

                            if (positionleft == 0.0) positionleft = 10.0;

                            tmptext.SetValue(Canvas.TopProperty, positiontop);

                            //checks for different display type and adds in that way
                            if (_legend.DisplayType == DisplayType.TextAndLine)
                            {
                                tmpline = new Line();

                                tmpline.Stroke = ds.Fill;
                                tmpline.StrokeThickness = 4;
                                tmpline.X1 = positionleft;
                                tmpline.X2 = positionleft + 15.0;
                                tmpline.Y1 = (double)tmptext.GetValue(Canvas.TopProperty) + tmptext.ActualHeight / 2;
                                tmpline.Y2 = tmpline.Y1;

                                positionleft += 20.0;

                                content.Children.Add(tmpline);
                            }
                            else
                            {
                                tmptext.Foreground = ds.Fill;
                            }


                            tmptext.SetValue(Canvas.LeftProperty, positionleft);
                            positionleft = (double)tmptext.GetValue(Canvas.LeftProperty) + tmptext.ActualWidth + 10.0;

                            maxwidth = positionleft;
                            maxheight = tmptext.ActualHeight + 20.0;

                            content.Children.Add(tmptext);

                        }
                        break;
                    }
                case Layout.Vertical:
                    {
                        foreach (DataSource ds in _datamodel)
                        {
                            tmptext = new TextBlock();
                            tmptext.Text = ds.Name;
                            tmptext.FontSize = 11;
                            tmptext.FontFamily = new FontFamily("Arial");

                            positionleft = 10.0;
                            tmptext.SetValue(Canvas.TopProperty, positiontop);

                            positiontop = positiontop + tmptext.ActualHeight + 10.0;

                            if (_legend.DisplayType == DisplayType.TextAndLine)
                            {
                                tmpline = new Line();

                                tmpline.Stroke = ds.Fill;
                                tmpline.StrokeThickness = 4;
                                tmpline.X1 = positionleft;
                                tmpline.X2 = positionleft + 15.0;
                                tmpline.Y1 = (double)tmptext.GetValue(Canvas.TopProperty) + tmptext.ActualHeight / 2;
                                tmpline.Y2 = tmpline.Y1;

                                content.Children.Add(tmpline);
                                positionleft += 20.0;
                            }
                            else
                            {
                                tmptext.Foreground = ds.Fill;
                            }

                            tmptext.SetValue(Canvas.LeftProperty, positionleft);

                            maxwidth = (double)tmptext.GetValue(Canvas.LeftProperty) + tmptext.ActualWidth + 10.0;
                            maxheight = (double)tmptext.GetValue(Canvas.TopProperty) + tmptext.ActualHeight + 10.0;

                            content.Children.Add(tmptext);
                        }


                        break;
                    }
            }

            rt.Width = maxwidth;
            rt.Height = maxheight;
            rt.Fill = _legend.Background;
            rt.RadiusX = 5.0;
            rt.RadiusY = 5.0;

            content.SetValue(Canvas.WidthProperty, rt.Width);
            content.SetValue(Canvas.HeightProperty, rt.Height);

            if (_legend.Layout == Layout.Vertical)
            {
                content.SetValue(Canvas.LeftProperty, _layoutroot.ActualWidth - (double)content.Width - 10.0);

                txt = _layoutroot.FindName("_titletext") as TextBlock;

                if (null != txt && (double)txt.GetValue(Canvas.TopProperty) + txt.ActualHeight > (_layoutroot.ActualHeight - (double)content.Height) / 2)
                    content.SetValue(Canvas.TopProperty, (double)txt.GetValue(Canvas.TopProperty) + txt.ActualHeight);
                else content.SetValue(Canvas.TopProperty, (_layoutroot.ActualHeight - (double)content.Height) / 2);
            }
            else
            {
                content.SetValue(Canvas.LeftProperty, (_layoutroot.ActualWidth - content.Width) / 2);
                content.SetValue(Canvas.TopProperty, _layoutroot.ActualHeight - content.Height - 10.0);
            }

            _layoutroot.Children.Add(content);

            UpdateMetrics();

            if (null != DrawAreaChanged) DrawAreaChanged();
        }

        /// <summary>
        /// Occurs when the mouse leaves the Legend Canvas
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnMouseLeave(object sender, MouseEventArgs e)
        {
            if (null != LegendMouseLeave) LegendMouseLeave(sender, e);
        }

        /// <summary>
        /// Occurs when the mouse enters the Legend Canvas
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnLegendEnter(object sender, MouseEventArgs e)
        {
            if (null != LegendMouseEnter) LegendMouseEnter(sender, e);
        }

        /// <summary>
        /// recalculates the metrics and updates the chart layout
        /// </summary>
        private void UpdateMetrics()
        {
            Canvas Legendc;
            TextBlock txt;

            AreaStartX = 0.0;

            Legendc = _layoutroot.FindName("_legendCanvas") as Canvas;
            txt = _layoutroot.FindName("_titletext") as TextBlock;

            if (null == txt) AreaStartY = 0.0;
            else AreaStartY = txt.ActualHeight + (double)txt.GetValue(Canvas.TopProperty);

            if (null == Legendc)
            {
                AreaHeight = _layoutroot.ActualHeight - AreaStartY;
                AreaWidth = base.ActualWidth;
            }
            else
            {
                if (_legend.Layout == Layout.Horizontal)
                {
                    AreaHeight = (double)Legendc.GetValue(Canvas.TopProperty) - AreaStartY;
                    AreaWidth = base.ActualWidth;
                }
                else
                {
                    AreaHeight = _layoutroot.ActualHeight - AreaStartY;
                    AreaWidth = (double)Legendc.GetValue(Canvas.LeftProperty);
                }
            }

            UserControl _ChartElement = _layoutroot.FindName("_ChartItem") as UserControl;

            if (null != _ChartElement)
            {
                _ChartElement.MaxHeight = AreaHeight;
                _ChartElement.MaxWidth = AreaWidth;

                _ChartElement.Height = AreaHeight;
                _ChartElement.Width = AreaWidth;

                _ChartElement.SetValue(Canvas.LeftProperty, AreaStartX);
                _ChartElement.SetValue(Canvas.TopProperty, AreaStartY);
            }
        }

        /// <summary>
        /// gets the controls background or sets it
        /// </summary>
        public Brush Background
        {
            get
            {
                return _background;
            }
            set
            {
                _background = value;

                _layoutroot.Background = _background;
            }
        }

        /// <summary>
        /// gets the controls forground used for the labels and title or sets it
        /// </summary>
        public Brush Foreground
        {
            get
            {
                return _foreground;
            }
            set
            {
                _foreground = value;
            }
        }

        /// <summary>
        /// Gets settings for Fontsyles
        /// </summary>
        public Font FontSettings
        {
            get { return _font; }
        }

        /// <summary>
        /// Gets settings for the Legend
        /// </summary>
        public Legend Legend
        {
            get { return _legend; }
        }

        /// <summary>
        /// This updates all Items, that are currently displayed.
        /// </summary>
        private void UpdateDisplayedItems()
        {
            Canvas Legendc;
            TextBlock txt;

            Legendc = _layoutroot.FindName("_legendCanvas") as Canvas;
            txt = _layoutroot.FindName("_titletext") as TextBlock;

            if (null != txt) txt.SetValue(Canvas.LeftProperty, ((double)ActualWidth - txt.ActualWidth) / 2);

            if (null != Legendc)
            {
                if (_legend.Layout == Layout.Horizontal)
                {
                    Legendc.SetValue(Canvas.TopProperty, ActualHeight - Legendc.ActualHeight);
                    Legendc.SetValue(Canvas.LeftProperty, (ActualWidth - Legendc.ActualWidth) / 2);
                }
                else
                {
                    Legendc.SetValue(Canvas.TopProperty, (ActualHeight - Legendc.ActualHeight) / 2);
                    Legendc.SetValue(Canvas.LeftProperty, ActualWidth - Legendc.ActualWidth - 10.0);
                }
            }

            UpdateMetrics();
        }

        /// <summary>
        /// Sets or gets the ItemType
        /// </summary>
        public ItemType ItemType
        {
            get
            {
                return _itemtype;
            }
            set
            {
                if (value == _itemtype) return;

                _itemtype = value;

                try
                {
                    //IEventImpl ie = (IEventImpl)_innerchart;
                    //ie.ItemChanged();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
            }
        }

        /// <summary>
        /// sets or gets the Animation used for the Control
        /// </summary>
        public Animation Animation
        {
            get
            {
                return _animation;
            }
            set
            {
                _animation = value;
            }
        }

        /// <summary>
        /// gets the LayoutRoot at the Control
        /// </summary>
        protected Canvas LayoutRoot
        {
            get { return _layoutroot; }
        }

        /// <summary>
        /// Property for the Useable Area
        /// It only contains Information in AxisCharts so far.
        /// </summary>
        protected UseableArea Area
        {
            get;
            set;
        }

        /// <summary>
        /// notifies classes to update
        /// </summary>
        protected void RaiseAreaChanged()
        {
            if(null != DrawAreaChanged) DrawAreaChanged();
        }
    }
}
